﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Comp_GeoData;

namespace Comp_Geo
{
    public class Triangulator
    {
        private List<LineSegment> mapLines;
        private List<Node> nodes;
        private int furthestX;

        #region Initilization

        /// <summary>
        /// Take in the lines of the map
        /// Set the furthest x point
        /// For each point in map lines 
        /// </summary>
        /// <param name="mapLines"></param>
        /// <param name="monsterPosition"></param>
        /// <param name="kittenPosition"></param>
        public Triangulator(List<LineSegment> mapLines, Vector2 monsterPosition, Vector2 kittenPosition)
        {
            this.mapLines = mapLines;

            List<Node> adjacentNodes;
            nodes = new List<Node>();

            setFurthestX();
            setInteriorPoints();

            #region Old Code
            //// Loop throught list of map lines and off set nodes by 1 x and 1 y
            //// This will cause errors if the lines are too tight, but being magnitudes of 100s it should cause minimanl complicaitons
            //foreach (LineSegment lineSegment in mapLines)
            //{
            //    #region Old Code
            //    //Vector2 v;
            //    //if (lineSegment.StartPoint.X == lineSegment.EndPoint.X)
            //    //{
            //    //    if (lineSegment.StartPoint.Y < lineSegment.EndPoint.Y)
            //    //    {
            //    //        v = new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y + 1);
            //    //    }
            //    //    else
            //    //    {
            //    //        v = new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y - 1);
            //    //    }
            //    //}
            //    //else
            //    //{
            //    //    if (lineSegment.StartPoint.X < lineSegment.EndPoint.X)
            //    //    {
            //    //        v = new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y + 1);
            //    //    }
            //    //    else
            //    //    {
            //    //        v = new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y - 1);
            //    //    }
            //    //}

            //    //nodes.Add(new Node(v));
            //    #endregion
            //    //nodes.Add(new Node(lineSegment.OffsetStartPoint));
            //}
            #endregion

            // Set the next node in the list, which will corrospond to the end point, to the first node in the adjacent list
            foreach (Node node in nodes)
            {
                adjacentNodes = new List<Node>();
                if (nodes.IndexOf(node) == 0) // If first node in the list
                {
                    adjacentNodes.Add(nodes[nodes.Count - 1]); // The last node in the list
                    adjacentNodes.Add(nodes[1]);
                }
                else if (nodes.IndexOf(node) == (nodes.Count - 1)) // Last node in the list
                {
                    adjacentNodes.Add(nodes[nodes.Count - 2]); // The second to last node
                    adjacentNodes.Add(nodes[0]); // The first node
                }
                else // Otherwise the node is in the middle of the list
                {
                    adjacentNodes.Add(nodes[nodes.IndexOf(node) - 1]); // The previous node
                    adjacentNodes.Add(nodes[nodes.IndexOf(node) + 1]); // The next node
                }

                node.AdjacentNodes = adjacentNodes;
            }

            nodes.Add(new Node(monsterPosition, null));
            nodes.Add(new Node(kittenPosition, null));

            #region Old Code
            //// Sets up the 
            //foreach(LineSegment lineSegment in mapLines)
            //{
            //    foreach(Node node in nodes)
            //    {
            //        if(lineSegment.StartPoint == node.Position)
            //        {
            //            foreach(Node node2 in nodes)
            //            {
            //                if(lineSegment.EndPoint == node2.Position)
            //                {
            //                    adjacentNodes = new List<Node>();
            //                    adjacentNodes.Add(node2);
            //                    node.AdjacentNodes = adjacentNodes;
            //                }
            //            }
            //        }
            //    }
            //}
            #endregion
        }

        #endregion

        #region Public Methods

        public Graph Triangulate()
        {
            Graph graph;
            bool collides;
            bool getOut = false;

            foreach (Node node in nodes)
            {

                foreach (Node node2 in nodes)
                {
                    collides = false;

                    if (node.Position == node2.Position)
                    {
                        continue;
                    }

                    // OMG this is really ugly, find a way to fix it in the future!
                    if (node.AdjacentNodes != null)
                    {
                        foreach (Node adjNode in node.AdjacentNodes)
                        {
                            if (adjNode.Position == node2.Position)
                                getOut = true;
                        }
                    }
                    if (getOut == true)
                    {
                        getOut = false;
                        continue;
                    }

                    LineSegment testLine = new LineSegment(node.Position, node2.Position);
                    foreach (LineSegment lineSegment in mapLines)
                    {
                        if (LineIntersectsLine(lineSegment, testLine) == true)
                        {
                            collides = true;
                        }
                    }

                    if (collides == false)
                    {
                        if (node.AdjacentNodes != null)
                        {
                            node.AdjacentNodes.Add(node2);
                        }
                        else
                        {
                            node.AdjacentNodes = new List<Node>();
                            node.AdjacentNodes.Add(node2);
                        }
                    }
                }

                foreach (Node node3 in node.AdjacentNodes)
                {
                    if (nodes.IndexOf(node) == nodes.Count - 2)
                    {
                        Console.WriteLine("Monster Contains NodeIndex:{0}", nodes.IndexOf(node3));
                    }

                    else if (nodes.IndexOf(node) == nodes.Count - 1)
                    {
                        Console.WriteLine("Kitten Contains NodeIndex:{0}", nodes.IndexOf(node3));
                    }

                    else
                        Console.WriteLine("NodeIndex:{0} Contains NodeIndex:{1}", nodes.IndexOf(node), nodes.IndexOf(node3));
                }
            }

            graph = new Graph(nodes);

            return graph;
        }

        #endregion

        #region Private Methods

        // Test if two line segments intersect
        // if they do return false
        private bool LineIntersectsLine(LineSegment lineSeg, LineSegment testLine)
        {
            Vector2 L1SP = lineSeg.StartPoint;
            Vector2 L1EP = lineSeg.EndPoint;
            Vector2 L2SP = testLine.StartPoint;
            Vector2 L2EP = testLine.EndPoint;

            float q = (L1SP.Y - L2SP.Y) * (L2EP.X - L2SP.X) - (L1SP.X - L2SP.X) * (L2EP.Y - L2SP.Y);
            float d = (L1EP.X - L1SP.X) * (L2EP.Y - L2SP.Y) - (L1EP.Y - L1SP.Y) * (L2EP.X - L2SP.X);

            if (d == 0)
            {
                return false;
            }

            float r = q / d;
            q = (L1SP.Y - L2SP.Y) * (L1EP.X - L1SP.X) - (L1SP.X - L2SP.X) * (L1EP.Y - L1SP.Y);
            float s = q / d;

            if (r < 0 || r > 1 || s < 0 || s > 1)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// For each point in map
        /// take four corner points and test its interorness to convex poly
        /// </summary>
        private void setInteriorPoints()
        {
            List<Vector2> tempPoints = new List<Vector2>();
            foreach (LineSegment lineSegment in mapLines)
            {
                if (pointInterior(new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y + 1)) == true)
                {
                    tempPoints.Add(new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y + 1));
                }
                if (pointInterior(new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y + 1)) == true)
                {
                    tempPoints.Add(new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y + 1));
                }
                if (pointInterior(new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y - 1)) == true)
                {
                    tempPoints.Add(new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y - 1));
                }
                if (pointInterior(new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y - 1)) == true)
                {
                    tempPoints.Add(new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y - 1));
                }

                if (tempPoints.Count > 1)
                {
                    if (pointInterior(new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y + 1)) == false)
                    {
                        nodes.Add(new Node(new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y - 1)));
                    }
                    if (pointInterior(new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y + 1)) == false)
                    {
                        nodes.Add(new Node(new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y - 1)));
                    }
                    if (pointInterior(new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y - 1)) == false)
                    {
                        nodes.Add(new Node(new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y + 1)));
                    }
                    if (pointInterior(new Vector2(lineSegment.StartPoint.X - 1, lineSegment.StartPoint.Y - 1)) == false)
                    {
                        nodes.Add(new Node(new Vector2(lineSegment.StartPoint.X + 1, lineSegment.StartPoint.Y + 1)));
                    }
                }
                else { nodes.Add(new Node(tempPoints[0])); }
                tempPoints.Clear();
            }
        }

        /// <summary>
        /// Checks if point is interior to the convex polygon
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        private bool pointInterior(Vector2 point)
        {
            int count = 0;
            LineSegment tempLineSeg = new LineSegment(point, new Vector2(furthestX + 1, point.Y));
            foreach (LineSegment lineSegment in mapLines)
            {
                if (LineIntersectsLine(lineSegment, tempLineSeg) == true)
                {
                    count++;
                }
            }

            if (count % 2 == 1) { return true; }
            else { return false; }
        }

        /// <summary>
        /// Sets furthes x point for line list
        /// Needed for raycasting
        /// </summary>
        private void setFurthestX()
        {
            furthestX = 0;
            foreach (LineSegment lineSegment in mapLines)
            {
                if (lineSegment.StartPoint.X > furthestX)
                {
                    furthestX = (int)lineSegment.StartPoint.X;
                }
            }
            //Console.WriteLine("Furthest Point is " + furthestX);
        }

        #endregion
    }
}